package com.fjb.tool.im.service;

import java.nio.charset.Charset;

import com.fjb.tool.im.service.handler.MyServerInboundHandler;
import com.fjb.tool.im.service.handler.MyServerOutboundHandler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @Description:即时通讯 netty 服务
 * @author hemiao
 * @time:2020年4月1日 下午9:26:30
 */
public class ImNettyServer {
	
	/**
	 * 端口号
	 */
	private int port = 8080;
	
	public void start() {
		// volitile： 防止指令重排序，因为java虚拟机在运行的时候，指令不是按照你写的执行顺序来排序的，加上volatile之后可以使得被修饰的属性按照原有顺序执行；
		// parent接收者：bossGroup	
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		// child客户端：WorkGroup
		EventLoopGroup workGroup = new NioEventLoopGroup();
		
		//	
		ServerBootstrap serverBootstrap = new ServerBootstrap();
		// 1、设置 EventLoopGroup 的  parent接收者，child客户端。    服务端的BootStrap，一个需要接受连接，一个用来处理事件
		// ServerBootstrap用一个ServerSocketChannelFactory 来实例化。ServerSocketChannelFactory 有两种选择，
		serverBootstrap.group(bossGroup, workGroup)
		// 2、Channel理解为通道，网络传输的通道类型  
		// 一种是NioServerSocketChannelFactory，一种是OioServerSocketChannelFactory。 前者使用NIO，
		// 后则使用普通的阻塞式IO。它们都需要两个线程池实例作为参数来初始化，一个是boss线程池，一个是worker线程池。
		// NioServerSocketChannelFactory只需要少量活动的worker线程及能很好的处理众多的channel，而OioServerSocketChannelFactory则需要与打开channel等量的worker线程来服务
		// 线 程是一种资源，所以当netty服务器需要处理长连接的时候，最好选择NioServerSocketChannelFactory，这样可以避免创建大 量的worker线程。在用作http服务器的时候，
		// 也最好选择NioServerSocketChannelFactory，因为现代浏览器都会使用 http keepalive功能（可以让浏览器的不同http请求共享一个信道），这也是一种长连接
		.channel(NioServerSocketChannel.class)
		// 3、
		.option(ChannelOption.SO_BACKLOG, 1024)
		// ChannerlHandler要被装进ChannelPipeline  , 而过程则是调用ChannelInitializer的iniChannel方法
		.childHandler(new ChannelInitializer<Channel>(){
			@Override
			protected void initChannel(Channel ch) throws Exception {
				// addLast，在PipeLine中的Handler是有顺序的，所以
                // 有addLast，addFirst等方法
                // 添加的同时，可以指定Encoder和Decoder
				// worker线程的生命周期（life circle）
				// 当某个channel有消息到达或者有消息需要写入socket的时候，worker线程就会从线程池中取出一个。在worker线程中，消息会经过设定好 的ChannelPipeline处理。ChannelPipeline就是一堆有顺序的filter，
				// 它分为两部分：UpstreamHandler和 DownStreamHandler。本文着重介绍netty的线程模型，所以关于pipeline的内容从简说明。
				// 客户端送入的消息会首先由许多UpstreamHandler依次处理，处理得到的数据送入应用的业务逻辑handler，通常用SimpleChannelUpstreamHandler来实现这一部分。
				ChannelPipeline pipeline = ch.pipeline();
				
				pipeline.addFirst(new MyServerInboundHandler());
				pipeline.addFirst(new MyServerOutboundHandler());
//				ch.pipeline().addLast(new RequestDecoder(),
//                        new ResponseDataEncoder(),
//                        new ProcessingHandler());
				
			}
		});
		try {
			// 负责绑定端口，当这个方法执行后，ServerBootstrap就可以接受指定端口上的socket连接了。一个ServerBootstrap可以绑定多个端口。 开始监听端口的socket请求
			ChannelFuture future = serverBootstrap.bind(port).sync();
			// 注册一个ChannelFutureListener，以便在操作完成时获得通知
			future.addListener(new ChannelFutureListener() {
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
						
					System.out.println("operationComplete");
					
					// 检查操作的状态
	                if (future.isSuccess()) {
	                    // 如果操作是成功的，则创建一个ByteBuf 以持有数据
	                    ByteBuf buffer = Unpooled.copiedBuffer("Hello", Charset.forName("UTF-8"));
	                    // 将数据异步地发送到远程节点。返回一个ChannelFuture
	                    // ChannelFuture wf = future.channel().write(buffer);
	                    // ...
	                }else if (future.isDone()) {
						// 操作是否完成
	                	
					}else if (future.isVoid()) {
						// 
						
					}else if (future.isCancellable()) {
						
						
					}else if (future.isCancelled()) {
						// 已完成的当前操作是否被取消
						
					}else {
	                    // 如果发生错误，则访问描述原因的Throwable
	                    Throwable cause = future.cause();
	                    cause.printStackTrace();
	                }
				}
			});
			
			System.out.println(" im netty server 启动成功");
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			 // 关闭所有事件循环以终止所有线程。
             bossGroup.shutdownGracefully();
             workGroup.shutdownGracefully();
		}
		
	}
	
	public static void main(String[] args) {
		
		new ImNettyServer().start();
	}
}
